package logger

import (
	"os"
	"strings"
	"sync"
	"time"

	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	logger           *MyLogger
	DirPath          string
	Level            = "debug"
	SaveFile         = false
	PrintConsole     = true
	ShowFullPath     = false // 控制是否显示完整路径
	Separator        = ""    // 控制日志消息中的分隔符
	PathMsgSeparator = true  // true 表示仅在路径和消息之间添加分隔符，false 表示在所有字段间添加分隔符
	UseJSONFormat    = false // 控制日志格式，false 为 console 格式，true 为 json 格式

	mu sync.RWMutex
)

// GetEnCoder 正常日志编码器
// 设置日志编码器
func GetEnCoder() zapcore.Encoder {
	config := zap.NewProductionEncoderConfig()
	// 设置日志中时间的格式
	config.EncodeTime = zapcore.ISO8601TimeEncoder
	// 设置日志中级别的格式
	config.EncodeLevel = zapcore.CapitalLevelEncoder
	// 路径显示方式
	if ShowFullPath {
		config.EncodeCaller = zapcore.FullCallerEncoder
	} else {
		config.EncodeCaller = zapcore.ShortCallerEncoder
	}
	// 按原样序列化记录器名称
	config.EncodeName = zapcore.FullNameEncoder
	// 动态添加分隔符到路径和消息之间
	if Separator != "" {
		originalCaller := config.EncodeCaller
		originalTime := config.EncodeTime
		originalLevel := config.EncodeLevel

		// 根据 PathMsgSeparator 动态调整分隔符插入逻辑
		if !PathMsgSeparator {
			config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
				originalTime(t, enc)
				enc.AppendString(Separator)
			}
			config.EncodeLevel = func(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
				originalLevel(l, enc)
				enc.AppendString(Separator)
			}
		}

		config.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
			originalCaller(caller, enc)
			enc.AppendString(Separator)
		}
	}

	// 根据 UseJSONFormat 选择编码器
	if UseJSONFormat {
		return zapcore.NewJSONEncoder(config)
	}
	return zapcore.NewConsoleEncoder(config)
}

// GetInfoWriterSyncer 生成普通信息的Writer
func GetInfoWriterSyncer(path string) zapcore.WriteSyncer {
	p := strings.TrimRight(path, `/`)
	// 引入第三方库 Lumberjack 加入日志切割功能
	infoLumberIO := &lumberjack.Logger{
		Filename:   p + "/info.log",
		MaxSize:    10, // megabytes
		MaxBackups: 3,
		MaxAge:     7,     // days
		Compress:   false, // Compress确定是否应该使用gzip压缩已旋转的日志文件。默认值是不执行压缩。
	}
	return zapcore.AddSync(infoLumberIO)
}

// GetErrorWriterSyncer 生成故障信息的Writer
func GetErrorWriterSyncer(path string) zapcore.WriteSyncer {
	p := strings.TrimRight(path, `/`)
	// 引入第三方库 Lumberjack 加入日志切割功能
	lumberWriteSyncer := &lumberjack.Logger{
		Filename:   p + "/error.log",
		MaxSize:    10, // megabytes
		MaxBackups: 3,
		MaxAge:     7,     // days
		Compress:   false, // Compress确定是否应该使用gzip压缩已旋转的日志文件。默认值是不执行压缩。
	}
	return zapcore.AddSync(lumberWriteSyncer)
}

func L() *MyLogger {
	if logger == nil {
		logger = Logger()
	}
	return logger
}

// MyLogger 是一个本地类型，嵌入 *zap.SugaredLogger 以扩展自定义方法
type MyLogger struct {
	*zap.SugaredLogger
}

// Success 记录成功日志，带✅图标
func (l *MyLogger) Success(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Info("✅ " + msg)
}

// Fail 记录失败日志，带❌图标
func (l *MyLogger) Fail(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Error("❌ " + msg)
}

func (l *MyLogger) DebugWithIcon(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Debug("🐛 " + msg)
}

func (l *MyLogger) DebugWithIconw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Debugw("🐛 "+msg, keysAndValues...)
}
func (l *MyLogger) DebugWithIconf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Debugf("🐛 "+format, args...)
}

func (l *MyLogger) InfoWithIcon(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Info("ℹ️ " + msg)
}

func (l *MyLogger) InfoWithIconw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Infow("ℹ️ "+msg, keysAndValues...)
}
func (l *MyLogger) InfoWithIconf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Infof("ℹ️ "+format, args...)
}

// WarnWithIcon 记录警告日志，带⚠️图标
func (l *MyLogger) WarnWithIcon(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Warn("⚠️ " + msg)
}

// FatalWithIcon 记录致命日志，带💀图标
func (l *MyLogger) FatalWithIcon(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Fatal("💀 " + msg)
}

// Successw 记录成功日志，带✅图标
func (l *MyLogger) Successw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Infow("✅ "+msg, keysAndValues...)
}

// Failw 记录失败日志，带❌图标
func (l *MyLogger) Failw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Errorw("❌ "+msg, keysAndValues...)
}

// WarnWithIconw 记录警告日志，带⚠️图标
func (l *MyLogger) WarnWithIconw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Warnw("⚠️ "+msg, keysAndValues...)
}

// FatalWithIconw 记录致命日志，带💀图标
func (l *MyLogger) FatalWithIconw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Fatalw("💀 "+msg, keysAndValues...)
}

// Successf 格式化成功日志，带✅图标
func (l *MyLogger) Successf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Infof("✅ "+format, args...)
}

// Failf 格式化失败日志，带❌图标
func (l *MyLogger) Failf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Errorf("❌ "+format, args...)
}

// WarnWithIconf 格式化警告日志，带⚠️图标
func (l *MyLogger) WarnWithIconf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Warnf("⚠️ "+format, args...)
}

// FatalWithIconf 格式化致命日志，带💀图标
func (l *MyLogger) FatalWithIconf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Fatalf("💀 "+format, args...)
}

func (l *MyLogger) PanicWithIcon(msg string) {
	l.WithOptions(zap.AddCallerSkip(1)).Panic("💥 " + msg)
}

func (l *MyLogger) PanicWithIconw(msg string, keysAndValues ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Panicw("💥 "+msg, keysAndValues...)
}

func (l *MyLogger) PanicWithIconf(format string, args ...interface{}) {
	l.WithOptions(zap.AddCallerSkip(1)).Panicf("💥 "+format, args...)
}

// 动态更新配置
func Update() {
	mu.Lock()
	defer mu.Unlock()

	logger = Logger()
}
func Logger() *MyLogger {
	level := map[string]zapcore.Level{
		"debug": zapcore.DebugLevel,
		"info":  zapcore.InfoLevel,
		"warn":  zapcore.WarnLevel,
		"error": zapcore.ErrorLevel,
		"fatal": zapcore.FatalLevel,
		"panic": zapcore.PanicLevel,
	}

	encoder := GetEnCoder()
	if DirPath == "" {
		DirPath = "."
	}
	infowriter := GetInfoWriterSyncer(DirPath)
	errwriter := GetErrorWriterSyncer(DirPath)
	lv := level[strings.ToLower(Level)]
	infolevel := zap.LevelEnablerFunc(
		func(l zapcore.Level) bool {
			return l < zap.WarnLevel && l >= lv
		},
	)
	errlevel := zap.LevelEnablerFunc(
		func(l zapcore.Level) bool {
			return l >= zap.WarnLevel && l >= lv
		},
	)
	infocore := zapcore.NewCore(encoder, infowriter, infolevel)
	errcore := zapcore.NewCore(encoder, errwriter, errlevel)
	consolecore := zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), level[strings.ToLower(Level)])
	var core []zapcore.Core
	if SaveFile {
		core = append(core, infocore)
		core = append(core, errcore)
	}
	if PrintConsole {
		core = append(core, consolecore)
	}
	logger := zap.New(zapcore.NewTee(core...), zap.AddCaller())
	return &MyLogger{logger.Sugar()}
}
